import java.applet.*;
import java.awt.*;
import java.lang.*;

public class ThreeDBrownian extends Applet implements Runnable
{
 private double rl,rh,tx1,ty1,tx2,ty2;
 private double rad,cost,sint;
 Dimension d;
 double bigx,bigy,bigxvel,bigyvel,mr=5.0;
 private int num=20;
 boolean frozen=false;
 private Graphics g,offscreen;
 Image offImage;
 Thread animatorThread;
 double[] x=new double[num+1];
 double[] xx=new double[num+1];
 double[] y=new double[num+1];
 double[] yy=new double[num+1];

 public void init()
{
 d=size();
 rl=d.width;
 rh=d.height;
 for (int i=1;i<=num;i++)
{
 x[i]=(Math.random()*(rl-20)+10);
 xx[i]=(Math.random()*5);
 y[i]=(Math.random()*(rh-20)+10);
 yy[i]=(Math.random()*5);
}
 bigx=(Math.random()*(rl-40)+20);
 bigxvel=(Math.random()*5);
 bigy=(Math.random()*(rh-40)+20);
 bigyvel=(Math.random()*5);
}
 
 public void start()
{
 if (!frozen)
{ 
 if (animatorThread == null) animatorThread = new Thread(this);
 animatorThread.start();
} 
}
 
 public void stop()
{
 animatorThread=null;
}
    
 public boolean mouseDown(Event e, double x, double y) 
{
  if (frozen) 
{
     frozen = false;
     start();
}
 else 
{
     frozen = true;
     stop();
}
 return true;
} 

 public void paint(Graphics g)
{
 update(g);
}
 
        public void update( Graphics g ) {
		double x0,xpos=100,y0,ypos=100,z0,zpos=100,B,I,shade;
		Dimension d=size();
                if (offscreen == null) {
		      offImage=createImage(d.width,d.height);
		      offscreen=offImage.getGraphics();
		}
		 offscreen.setColor(Color.black);
 		 offscreen.fillRect(0,0,(int)rl,(int)rh);
 		 offscreen.setColor(Color.black);
   		 offscreen.drawRect(0,0,(int)rl-1,(int)rh-1);
 		 offscreen.drawRect(1,1,(int)rl-2,(int)rh-2);
		 for(int i=1;i<=num;i++)
                 {
  for (x0=-5;x0<=5;x0++)
  for (y0=-(int)(Math.sqrt(25-x0*x0));y0<=(int)(Math.sqrt(25-x0*x0));y0++)
     {
      z0=(int)(Math.sqrt(25-x0*x0-y0*y0));  
    B=Math.sqrt((x[i]-xpos)*(x[i]-xpos)+(y[i]-ypos)*(y[i]-ypos)+(zpos*zpos));
                  B=B*Math.sqrt(x0*x0+y0*y0+z0*z0);
                  I=((x[i]-xpos)*x0 + (y[i]-ypos)*y0 - zpos*z0)/B;
                  shade=(int)(254.9*I);
                  if (shade > 0 ) shade=0;    
//		 offscreen.setColor(Color.red);
		 offscreen.setColor(new Color((int)(-shade),0,0));
// 		 offscreen.fillOval((int)x[i]-5,(int)y[i]-5,10,10);
 		 offscreen.fillOval((int)(x[i]+x0),(int)(y[i]+y0),1,1);
	}
		}
// 		offscreen.setColor(Color.blue);
// 		offscreen.fillOval((int)bigx-10,(int)bigy-10,20,20);
  for (x0=-10;x0<=10;x0++)
  for (y0=-(int)(Math.sqrt(100-x0*x0));y0<=(int)(Math.sqrt(100-x0*x0));y0++)
     {
      z0=(int)(Math.sqrt(100-x0*x0-y0*y0));  
     B=Math.sqrt((bigx-xpos)*(bigx-xpos)+(bigy-ypos)*(bigy-ypos)+(zpos*zpos));
                  B=B*Math.sqrt(x0*x0+y0*y0+z0*z0);
                  I=((bigx-xpos)*x0 + (bigy-ypos)*y0 - zpos*z0)/B;
                  shade=(int)(254.9*I);
                  if (shade > 0 ) shade=0;    
//		 offscreen.setColor(Color.red);
		 offscreen.setColor(new Color(0,0,(int)(-shade)));
// 		 offscreen.fillOval((int)x[i]-5,(int)y[i]-5,10,10);
 		 offscreen.fillOval((int)(bigx+x0),(int)(bigy+y0),1,1);
	}
                g.drawImage(offImage,0,0,this);
        }

 public void run()
{
  while (Thread.currentThread()==animatorThread)
{
  update(this.getGraphics());
  for (int i=1;i<=num;i++)
{ 
  for (int k=1;k<=num;k++)
    if(k!=i)
{
  rad= Math.sqrt((x[i]-x[k])*(x[i]-x[k]) + (y[i]-y[k])*(y[i]-y[k]));
  if (rad<=10)
{
     tx1=xx[k];ty1=yy[k];tx2=xx[i];ty2=yy[i];
     cost=(x[k]-x[i])/rad;
     sint=(y[k]-y[i])/rad;
     xx[k]=(tx2*cost-ty1*sint);
     xx[i]=(tx1*cost-ty2*sint);
     yy[k]=(tx2*sint + ty1*cost);
     yy[i]=(tx1*sint + ty2*cost);      
}

}

  rad= Math.sqrt((x[i]-bigx)*(x[i]-bigx) + (y[i]-bigy)*(y[i]-bigy));
  if (rad<=15)
{
     tx1=bigxvel;ty1=bigyvel;tx2=xx[i];ty2=yy[i];
     cost=(bigx-x[i])/rad;
     sint=(bigy-y[i])/rad;
     bigxvel=(tx2*cost/mr-ty1*sint);
     xx[i]=(mr*tx1*cost-ty2*sint);
     bigyvel=(tx2*sint/mr + ty1*cost);
     yy[i]=(tx1*sint*mr + ty2*cost);      
}

  if ( x[i]<=(rl-10) && x[i] >=10) x[i]+=xx[i];
  else 
{ 
   if(x[i]<10)xx[i]=Math.abs(xx[i]);
   if(x[i]>rl-10)xx[i]=-Math.abs(xx[i]);
    x[i]+=2*xx[i];
}  
  if ( y[i]<(rh-10) && y[i] >10) y[i]+=yy[i];
  else 
{ 
   if(y[i]<10)yy[i]=Math.abs(yy[i]);
   if(y[i]>rh-10)yy[i]=-Math.abs(yy[i]);
    y[i]+=2*yy[i];
}  
}
  if ( bigx<=(rl-20) && bigx >=20) bigx+=bigxvel;
  else 
{ 
   if(bigx<20)bigxvel=Math.abs(bigxvel);
   if(bigx>rl-20)bigxvel=-Math.abs(bigxvel);
    bigx+=2*bigxvel;
}  
  if ( bigy<(rh-10) && bigy>10) bigy+=bigyvel;
  else 
{ 
   if(bigy<20)bigyvel=Math.abs(bigyvel);
   if(bigy>rh-20)bigyvel=-Math.abs(bigyvel);
    bigy+=2*bigyvel;
}  

//  for (int j=1;j<100000;j++);
}
}

} 
